package qq2564874169.tuaotuao.fx.orm.query;

import qq2564874169.tuaotuao.fx.EventDispatcher;
import qq2564874169.tuaotuao.fx.orm.*;
import qq2564874169.tuaotuao.fx.orm.where.QueryWhere;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public abstract class AbstractEntityQuery extends AbstractDbEntityCore implements DbEntityQuery {
    private static final String transBeforeKey = UUID.randomUUID().toString();
    private static final String transAfterKey = UUID.randomUUID().toString();
    private Map<String, Object> data = new HashMap<>();
    private EventDispatcher<TransFinishArgs> tranBefore = new EventDispatcher<>(transBeforeKey);
    private EventDispatcher<TransFinishArgs> tranAfter = new EventDispatcher<>(transAfterKey);
    private String eidTransBefore;
    private String eidTransAfter;
    private SqlQuery query;

    public AbstractEntityQuery(SqlQuery query) {
        super(query);
        this.query = query;
        eidTransBefore = query.evTransactionBefore().add((s, e) -> onTransactionBefore(e));
        eidTransAfter = query.evTransactionAfter().add((s, e) -> onTransactionAfter(e));
    }

    protected abstract <T> SqlParam onGets(QueryWhere<T> where, Integer timeout);

    protected abstract <T> SqlParam onGet(QueryWhere<T> where, Integer timeout);

    protected abstract <T> SqlParam onCount(QueryWhere<T> where, Integer timeout);

    protected void onTransactionBefore(TransFinishArgs args) {
        tranBefore.fire(transBeforeKey, this, args);
    }

    protected void onTransactionAfter(TransFinishArgs args) {
        tranAfter.fire(transAfterKey, this, args);
    }

    @Override
    public Map<String, Object> userdata() {
        return data;
    }

    @Override
    public String paramName(String name) {
        return query.paramName(name);
    }

    @Override
    public String escapeField(String field) {
        return query.escapeField(field);
    }

    @Override
    public void changeDataSource(String key) {
        query.changeDataSource(key);
    }

    @Override
    public <T> T get(QueryWhere<T> where, Integer timeout) {
        if (where.forUpdate() != ForUpdate.NoLock && inTransaction() == false) {
            throw new RuntimeException("行锁查询必须在事务中。");
        }
        SqlParam param = onGet(where, timeout);
        return query.queryOne(where.getTable(), param);
    }

    @Override
    public <T> List<T> gets(QueryWhere<T> where, Integer timeout) {
        if (where.forUpdate() != ForUpdate.NoLock && inTransaction() == false) {
            throw new RuntimeException("行锁查询必须在事务中。");
        }
        SqlParam param = onGets(where, timeout);
        return query.query(where.getTable(), param);
    }

    @Override
    public <T> long count(QueryWhere<T> where, Integer timeout) {
        SqlParam param = onCount(where, timeout);
        return query.queryOne(Long.class, param);
    }

    @Override
    public <T> CursorReader<T> cursor(QueryWhere<T> where, int fetchSize, boolean independent, Integer timeout) {
        if (where.forUpdate() != ForUpdate.NoLock && inTransaction() == false) {
            throw new RuntimeException("行锁查询必须在事务中。");
        }
        SqlParam param = onGets(where, timeout);
        return query.cursor(where.getTable(), param, fetchSize, independent);
    }

    @Override
    public <T> PageData<T> page(QueryWhere<T> where, Integer timeout) {
        if (where.take() == null || where.skip() == null) {
            throw new RuntimeException("分页查询必须设置skip和take");
        }
        if (where.skip() > 0 && where.skip() % where.take() != 0) {
            throw new RuntimeException("分页查询中skip必须能被take整除");
        }
        int page = where.skip() / where.take() + 1;
        PageSet ps = new PageSet(page, where.take());
        QueryWhere<T> newWhere = where.clone(s -> {
            s.set("skip", null);
            s.set("take", null);
            s.set("forUpdate", ForUpdate.NoLock);
        });
        SqlParam param = onGets(newWhere, timeout);
        return query.pager(where.getTable(), ps, param);
    }

    @Override
    public EventDispatcher<TransFinishArgs> evTransactionBefore() {
        return tranBefore;
    }

    @Override
    public EventDispatcher<TransFinishArgs> evTransactionAfter() {
        return tranAfter;
    }

    @Override
    public Transaction transaction(TransactionLevel level) {
        return query.transaction(level);
    }

    @Override
    public boolean inTransaction() {
        return query.inTransaction();
    }

    @Override
    public void close() {
        if (tranAfter != null) {
            tranAfter.clear();
            tranAfter = null;
        }
        if (tranBefore != null) {
            tranBefore.clear();
            tranBefore = null;
        }
        if (data != null) {
            data.clear();
            data = null;
        }
        if (query != null) {
            if (query.isClosed() == false) {
                query.evTransactionAfter().remove(eidTransAfter);
                query.evTransactionBefore().remove(eidTransBefore);
            }
            query = null;
        }
    }
}
